/**
 *  This file is part of PennerBot
 *
 *  Copyright (C) 2010 microkernel
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <core/connection.hpp>
#include <core/messages.hpp>

#include <iostream>

namespace PBot
{
namespace Core
{

Connection::Connection(Config* _cfg, P_StatusListener& _listener)
 : cfg(_cfg), listener(_listener), headers(NULL), outfile(NULL)
{
}

Connection::~Connection()
{
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
}

bool Connection::Init()
{
    curl = curl_easy_init();

    if (curl == NULL)
    {
        listener.Dispatch(cfg->id, MSG_CON_INITFAIL);
        return false;
    }

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Connection::WriteBuffer);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
    curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, cfg->userAgent.c_str());
    curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip,deflate");
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
    curl_easy_setopt(curl, CURLOPT_AUTOREFERER, true);
    curl_easy_setopt(curl, CURLOPT_HEADER, false);
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
    curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10);

    //TODO
    headers = curl_slist_append(headers, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    headers = curl_slist_append(headers, "Accept-Language: en-us,en;q=0.5");
    headers = curl_slist_append(headers, "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7");
    headers = curl_slist_append(headers, "Keep-Alive: 300");
    headers = curl_slist_append(headers, "Connection: keep-alive");

    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    if(!cfg->proxy.empty())
    {
        curl_easy_setopt(curl, CURLOPT_PROXY, cfg->proxy.c_str());
    }

    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

    return true;
}

bool Connection::Get(const std::string& url, bool setreferer)
{
    buffer = "";
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

    res = curl_easy_perform(curl);

    if(res != CURLE_OK)
    {
        GetLastError(url);
        return false;
    }

    if(setreferer)
    {
        SetReferer(url);
    }

    return true;
}

bool Connection::GetFile(const std::string& url, const std::string& filename, bool setreferer)
{
    if ((outfile = fopen(filename.c_str(), "wb")) == NULL)
    {
        return false;
    }

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Connection::WriteFile);
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

    res = curl_easy_perform(curl);
    fclose(outfile);

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Connection::WriteBuffer);

    if (res != CURLE_OK)
    {
        GetLastError(url);
        return false;
    }

    if(setreferer)
    {
        SetReferer(url);
    }

    return true;
}

bool Connection::Post(const std::string& url, const std::string& urlencoded, bool setreferer)
{
    buffer = "";
    curl_easy_setopt(curl, CURLOPT_POST, true);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, urlencoded.c_str());
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

    res = curl_easy_perform(curl);
    curl_easy_setopt(curl, CURLOPT_POST, false);

    curl_easy_setopt(curl, CURLOPT_HTTPGET, true);

    if(res != CURLE_OK)
    {
        GetLastError(url);
        return false;
    }

    if(setreferer)
    {
        SetReferer(url);
    }

    return true;
}

void Connection::SetReferer(const std::string& ref)
{
    curl_easy_setopt(curl, CURLOPT_REFERER, ref.c_str());
}

std::string Connection::Escape(const std::string& data)
{
    char* tmpStr = curl_easy_escape(curl, data.c_str(), data.length());

    if(tmpStr == NULL)
    {
        listener.Dispatch(cfg->id, MSG_CURL_ESCAPEERR, data);
    }

    std::string outStr(tmpStr);

    size_t offset=0;
    while((offset = outStr.find("%20", offset)) != std::string::npos)
    {
        outStr.replace(offset, 3, 1, '+');
        ++offset;
    }

    curl_free(tmpStr);

    return outStr;
}

const std::string& Connection::GetBuffer() const
{
    return buffer;
}

void Connection::GetLastError(const std::string& url)
{
    listener.Dispatch(cfg->id, MSG_CON_ERROR, url);
    listener.Dispatch(cfg->id, MSG_CURL_ERROR, std::string(curl_easy_strerror(res)));
}

size_t Connection::RealWriteBuffer(void *_buffer, size_t size, size_t nmemb)
{
    int result = size * nmemb;

    buffer.append(reinterpret_cast<char*>(_buffer), result);

    return result;
}

size_t Connection::RealWriteFile(void *_buffer, size_t size, size_t nmemb)
{
    fwrite(reinterpret_cast<char*>(_buffer), size, nmemb, outfile);
    return size * nmemb;
}

size_t Connection::WriteBuffer(void *_buffer, size_t size, size_t nmemb, void *userp)
{
    return static_cast<Connection*>(userp)->RealWriteBuffer(_buffer, size, nmemb);
}

size_t Connection::WriteFile(void *_buffer, size_t size, size_t nmemb, void *userp)
{
    return static_cast<Connection*>(userp)->RealWriteFile(_buffer, size, nmemb);
}

}
}
